﻿using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using UnityEditor;
using UnityEngine;

/// <summary>
/// UnityTools类，包含各个功能都能使用的功能，同时也管理UnityTools菜单，这是里菜单惟一入口 
/// </summary>
public class CommonTools : EditorWindow
{
    /// <summary>
    /// 版本号说明
    /// 第一个是大版本号，较大改动修改这个值。修改这个值会将后面的值重置为0 
    /// 第二个是小版本号，增加一些小功能修改这个值。修改这个值会将后面的值重置为0 
    /// 第三个是编译号，修复BUG、调整功能的时候修改这个值
    /// </summary>
    public static string Version = "Ver 1.1.5";

    /// <summary>
    /// 打包后，apk存放的文件夹（绝对路径）
    /// </summary>
    private static string buildApkFolder = System.Environment.CurrentDirectory + "/tempApk/";

    private static BuildTargetGroup[] buildTargetGroups = new BuildTargetGroup[]
    {
                BuildTargetGroup.Standalone,
                BuildTargetGroup.WebGL,
                BuildTargetGroup.Android,
                BuildTargetGroup.iOS,
                BuildTargetGroup.WSA,
    };

    private static BuildTargetGroup[] mobileBuildTargetGroups = new BuildTargetGroup[]
        {
                BuildTargetGroup.Android,
                BuildTargetGroup.iOS,
                BuildTargetGroup.PSM,
                BuildTargetGroup.Tizen,
                BuildTargetGroup.WSA
        };

    /// <summary>
    /// 宏列表，该表里面的宏在同一个版本中只能存在一个 
    /// </summary>
    private static string[] defineStr = { "GAME_DEBUG", "GAME_RELEASE" };

    [MenuItem("UnityTools/SVN/提交 Assets", false, 1)]
    [MenuItem("Assets/SVN/提交项目工程", false, 1)]
    private static void SvnCommitAssets()
    {
        RunCmd("TortoiseProc.exe", string.Format("/command:commit /path:\"{0}\\{1}\" /closeonend:0", System.Environment.CurrentDirectory, "Assets"));
    }

    [MenuItem("UnityTools/SVN/提交 ProjectSettings", false, 2)]
    private static void SvnCommitProjectSettings()
    {
        RunCmd("TortoiseProc.exe", string.Format("/command:commit /path:\"{0}\\{1}\" /closeonend:0", System.Environment.CurrentDirectory, "ProjectSettings"));
    }

    [MenuItem("UnityTools/SVN/更新 Assets", false, 3)]
    [MenuItem("Assets/SVN/更新项目工程", false, 1)]
    private static void SvnUpdateAssets()
    {
        RunCmd("TortoiseProc.exe", string.Format("/command:update /path:\"{0}\\{1}\" /closeonend:0", System.Environment.CurrentDirectory, "Assets"));
    }

    [MenuItem("UnityTools/SVN/更新 ProjectSettings", false, 4)]
    private static void SvnUpdateProjectSettings()
    {
        RunCmd("TortoiseProc.exe", string.Format("/command:update /path:\"{0}\\{1}\" /closeonend:0", System.Environment.CurrentDirectory, "ProjectSettings"));
    }

    #region 提交指定文件

    [MenuItem("Assets/SVN/提交指定文件或文件夹", true, 51)]
    private static bool CheckCommit()
    {
        if (Selection.activeObject == null)
            return false;
        else
            return true;
    }

    [MenuItem("Assets/SVN/提交指定文件或文件夹")]
    private static void SvnCommitThisFile()
    {
        RunCmd("TortoiseProc.exe", string.Format("/command:commit /path:\"{0}\\{1}\" /closeonend:0", System.Environment.CurrentDirectory, AssetDatabase.GetAssetPath(Selection.activeObject)));
    }

    #endregion

    #region 更新指定文件

    [MenuItem("Assets/SVN/更新指定文件或文件夹", true, 51)]
    private static bool CheckUpdate()
    {
        if (Selection.activeObject == null)
            return false;
        else
            return true;
    }

    [MenuItem("Assets/SVN/更新指定文件或文件夹")]
    private static void SvnUpdateThisFile()
    {
        RunCmd("TortoiseProc.exe", string.Format("/command:update /path:\"{0}\\{1}\" /closeonend:0", System.Environment.CurrentDirectory, AssetDatabase.GetAssetPath(Selection.activeObject)));
    }

    #endregion

    [MenuItem("UnityTools/创建规范化文件夹", false, 51)]
    private static void CreateFile()
    {
        FolderManager.ShowWindow();
    }

    [MenuItem("UnityTools/打开缓存目录", false, 101)]
    private static void OpenDirectory()
    {
        Process.Start(Application.persistentDataPath + "/");
    }

    [MenuItem("UnityTools/清空数据缓存目录", false, 102)]
    private static void ClearData()
    {
        bool flag = EditorUtility.DisplayDialog("UnityTools", "是否清空数据缓存目录？\n\n请确认已经将缓存数据替换为永久数据了。\n\n一旦清空无法恢复。", "确定", "取消");
        if (flag)
        {
            DirectoryInfo di = new DirectoryInfo(Application.persistentDataPath);
            di.Delete(true);
        }
    }

    [MenuItem("UnityTools/用缓存数据替换永久数据", false, 103)]
    private static void ReplaceFile()
    {
        string sourcePath = Application.persistentDataPath + "/";
        string targetPath = Directory.GetCurrentDirectory() + "/Assets/Resources/Data/";
        FileInfo[] fileName = new DirectoryInfo(sourcePath).GetFiles();
        for (int i = 0, count = fileName.Length; i < count; i++)
        {
            File.Copy(sourcePath + fileName[i].Name, targetPath + fileName[i].Name, true);
        }
        EditorUtility.DisplayDialog("UnityTools", "用缓存数据替换永久数据，替换成功。\n\n共替换了" + fileName.Length + "个文件。\n\n请手动属性Project视图！", "确定");
    }

    [MenuItem("UnityTools/版本切换/切换为调试版", true)]
    private static bool CheckDebug()
    {
        return !GetDefinesList(mobileBuildTargetGroups[0]).Contains(defineStr[0]);
    }

    [MenuItem("UnityTools/版本切换/切换为调试版")]
    private static void SetDebug()
    {
        SetEnabled(defineStr[0], true);
    }

    [MenuItem("UnityTools/版本切换/切换为发行版", true)]
    private static bool CheckRelease()
    {
        return !GetDefinesList(mobileBuildTargetGroups[0]).Contains(defineStr[1]);
    }

    [MenuItem("UnityTools/版本切换/切换为发行版")]
    private static void SetRelease()
    {
        SetEnabled(defineStr[1], true);
    }

    [MenuItem("UnityTools/帮助/关于本插件 %H", false)]
    private static void AboutPlugin()
    {
        bool flag = EditorUtility.DisplayDialog("关于本插件", "插件版本：" + Version + "\n\n作者：lzc\n\n主页：http://www.showlzc.vip", "访问官网", "关闭");
        if (flag)
        {
            //VersionsLog.ShowWindow();
            Application.OpenURL("http://www.showlzc.vip");
        }
        else
        {
            //Application.OpenURL("http://www.lzcspace.com");
        }
    }

    [MenuItem("UnityTools/帮助/访问官网", false)]
    private static void GoToWeb()
    {
        Application.OpenURL("http://www.showlzc.vip");
    }

    [MenuItem("UnityTools/帮助/版本日志", false)]
    private static void VersionsShow()
    {
        VersionsLog.ShowWindow();
    }

    [MenuItem("UnityTools/GameObject批量重命名工具", false, 151)]
    private static void GameObjectReNameShow()
    {
        GameObjectRename.ShowWindow();
    }

    [MenuItem("UnityTools/打包须知（第一次打包必看）", false, 201)]
    private static void CreateApkNameToClipboard()
    {
        EditorUtility.DisplayDialog("", "", "");
    }

    [MenuItem("UnityTools/打开APK目录", false, 201)]
    private static void OpenApkTempDirectory()
    {
        if (!Directory.Exists(buildApkFolder))
        {
            Directory.CreateDirectory(buildApkFolder);
        }

        Process.Start(buildApkFolder);
    }

    [MenuItem("UnityTools/打包Apk/打包测试版本APK", false, 202)]
    private static void BuildDebugApk()
    {
        EditorUserBuildSettings.development = true;
        EditorUserBuildSettings.connectProfiler = true;
        BuildApkSetting(string.Format("{0}_Debug_V{1}_{2}.apk", Application.productName, Application.version, System.DateTime.Now.ToString("MMdd")));
    }

    public static void BuildApkSetting(string _apkname)
    {
        if (!Directory.Exists(buildApkFolder))
        {
            Directory.CreateDirectory(buildApkFolder);
        }

        PlayerSettings.Android.keystorePass = "123456";
        PlayerSettings.Android.keyaliasPass = "123456";

#if UNITY_5_1   // unity 5.1 版本可用 
        List<string> levels = new List<string>();

        foreach (EditorBuildSettingsScene scene in EditorBuildSettings.scenes)
        {
            if (!scene.enabled) continue;
            levels.Add(scene.path);
        }
        
        if (BuildPipeline.BuildPlayer(levels.ToArray(), buildApkFolder + _apkname, BuildTarget.Android, BuildOptions.None).IsNullOrEmpty())
            Process.Start(buildApkFolder);
#elif UNITY_5_5 
        BuildPlayerOptions op = new BuildPlayerOptions();
        op.locationPathName = buildApkFolder + _apkname;
        op.options = BuildOptions.None;
        op.target = BuildTarget.Android;

        List<string> levels = new List<string>();
        foreach (EditorBuildSettingsScene scene in EditorBuildSettings.scenes)
        {
            if (!scene.enabled) continue;
            levels.Add(scene.path);
        }
        op.scenes = levels.ToArray();

        if (string.IsNullOrEmpty(BuildPipeline.BuildPlayer(op)))
            Process.Start(buildApkFolder);
#endif
    }

    [MenuItem("UnityTools/打包Apk/打包发行版本APK", false, 203)]
    private static void BuildReleaseApk()
    {
        EditorUserBuildSettings.development = false;
        EditorUserBuildSettings.connectProfiler = false;
        BuildApkSetting(string.Format("{0}_Release_V{1}_{2}.apk", Application.productName, Application.version, System.DateTime.Now.ToString("MMdd")));
    }

    [MenuItem("UnityTools/工程便签", false, 301)]
    private static void OpenNote()
    {
        ProjectNote.ShowWindow();
    }

    [MenuItem("UnityTools/设置某节点下所有Animation组件为不自动播放", false, 152)]
    private static void SetAnimationPlayAutomatically()
    {
        Animation[] _ani = Selection.activeGameObject.transform.GetComponentsInChildren<Animation>(true);

        for (int i = 0, icount = _ani.Length; i < icount; i++)
        {
            _ani[i].playAutomatically = false;
        }
    }

    [MenuItem("UnityTools/合并网格", true, 154)]
    [MenuItem("UnityTools/设置某节点下所有Animation组件为不自动播放", true, 152)]
    [MenuItem("UnityTools/设置某节点下所有MeshRender组件为不接受阴影", true, 153)]
    private static bool HaveSelectObject()
    {
        return Selection.activeGameObject == null ? false : true;
    }

    [MenuItem("UnityTools/设置某节点下所有MeshRender组件为不接受阴影", false, 153)]
    private static void RendererNoShadows()
    {
        Renderer[] _render = Selection.activeGameObject.transform.GetComponentsInChildren<Renderer>(true);

        for (int i = 0, icount = _render.Length; i < icount; i++)
        {
            _render[i].shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
            _render[i].receiveShadows = false;
        }
    }

    [MenuItem("UnityTools/合并MeshFilter网格", false, 154)]
    private static void CombineMeshes()
    {
        if (Selection.activeGameObject.transform.childCount <= 0)
        {
            UnityEngine.Debug.LogError("该节点下的子节点为 0，合并失败");
            return;
        }

        MeshFilter[] meshFilters = Selection.activeGameObject.GetComponentsInChildren<MeshFilter>();
        CombineInstance[] combine = new CombineInstance[meshFilters.Length];

        MeshRenderer[] meshRenderer = Selection.activeGameObject.GetComponentsInChildren<MeshRenderer>();  //获取自身和所有子物体中所有MeshRenderer组件
        Material[] mats = new Material[meshRenderer.Length];                    //新建材质球数组

        for (int i = 0; i < meshFilters.Length; i++)
        {

            mats[i] = meshRenderer[i].sharedMaterial;                           //获取材质球列表

            combine[i].mesh = meshFilters[i].sharedMesh;
            combine[i].transform = meshFilters[i].transform.localToWorldMatrix;
            meshFilters[i].gameObject.SetActive(false);
        }

        MeshFilter filterTransform = Selection.activeGameObject.GetComponent<MeshFilter>();
        if (filterTransform == null)
            Selection.activeGameObject.gameObject.AddComponent<MeshFilter>();
        MeshRenderer renderTransform = Selection.activeGameObject.GetComponent<MeshRenderer>();
        if (renderTransform == null)
            Selection.activeGameObject.gameObject.AddComponent<MeshRenderer>();

        filterTransform.sharedMesh = new Mesh();
        filterTransform.sharedMesh.CombineMeshes(combine, false);//为mesh.CombineMeshes添加一个 false 参数，表示并不是合并为一个网格，而是一个子网格列表

        renderTransform.sharedMaterials = mats;          //为合并后的GameObject指定材质

        filterTransform.gameObject.SetActive(true);
    }

    [MenuItem("UnityTools/取消勾选场景中所有UI组件中的RaycastTarget字段", false, 155)]
    private static void CancleRaycastTarget()
    {
        UnityEngine.UI.Graphic[] raycast = GameObject.FindObjectsOfType<UnityEngine.UI.Graphic>();
        for (int i = 0, count = raycast.Length; i < count; i++)
        {
            raycast[i].raycastTarget = false;
        }
        UnityEngine.Debug.Log("完成！");
    }

    [MenuItem("UnityTools/清空Win平台的PlayerPrefs数据", false, 251)]
    private static void ClearPlayerPrefs()
    {
        if (System.Text.RegularExpressions.Regex.IsMatch(Application.companyName, @"[\u4e00-\u9fa5]"))
        {
            UnityEngine.Debug.Log("公司名中包含中文，不能删除！");
            return;
        }
        else
        {
            // 需要保证公司名为英文 
            PlayerPrefs.DeleteAll();
            UnityEngine.Debug.Log("删除PlayerPrefs数据成功!");
        }
    }

    /// <summary>
    /// 运行外部程序 
    /// </summary>
    /// <param name="cmdExe">指定应用程序的完整路径，如果该程序在系统环境变量中，只需要填写对用的程序名称就可以</param>
    /// <param name="cmdStr">执行命令行参数</param>
    private static bool RunCmd(string cmdExe, string cmdStr, bool iswait = true)
    {
        bool result = false;
        try
        {
            using (Process myPro = new Process())
            {
                //指定启动进程是调用的应用程序和命令行参数
                ProcessStartInfo psi = new ProcessStartInfo(cmdExe, cmdStr);
                myPro.StartInfo = psi;
                myPro.Start();
                // 是否加上这句话，看个人需求。如果加上的话，我们必须关掉弹出的SVN窗口才能继续操作。如果不加上，则可以弹出SVN，也可以继续修改unity项目。个人建议加上比较好
                if (iswait)
                    myPro.WaitForExit();
                result = true;
            }
        }
        catch
        {

        }
        return result;
    }

    private static void SetEnabled(string defineName, bool mobile)
    {
        foreach (BuildTargetGroup group in mobile ? mobileBuildTargetGroups : buildTargetGroups)
        {
            var defines = GetDefinesList(group);
            foreach (var str in defineStr)
            {
                if (defines.Contains(str))
                {
                    defines.Remove(str);
                }
            }
            defines.Add(defineName);
            string definesString = string.Join(";", defines.ToArray());
            PlayerSettings.SetScriptingDefineSymbolsForGroup(group, definesString);
        }
    }

    private static List<string> GetDefinesList(BuildTargetGroup group)
    {
        return new List<string>(PlayerSettings.GetScriptingDefineSymbolsForGroup(group).Split(';'));
    }
}
